iT邦幫忙

2021 iThome 鐵人賽

DAY 22
1

相信大家或多或少都有去公家機關辦事的經驗。去公家機關辦事時,如果等待時間拖太久,肯定覺得很煩吧?好不容易等到了,這時萬一辦事員再來一句:「資料不足,回家補足再來!」哇,一下午就這樣去了!「資料不足這種小事,早點跟我說不行嗎?」離開時口中不自覺嘮叨的經驗,肯定也不少有吧?


號碼牌示意圖,圖片截自蝦皮購物

今天我們要來實際預演一下,看看在收到一個需求後,到底要怎麼安排 Clean Architecture 的四層架構。

人生如戲

程式,就是讓電腦去解決你真實世界發生的問題。而根據引用的不同理論,或是使用了不同的設計方式,解決的方式也不同。譬如「物件導向」程式,則是試圖將真實世界參與一件事情的人事物,都用「物件與物件間的互動」來模擬。我知道很抽像。沒關係,最好理解抽象描述的方法就是舉例!

我們回到許久不見的教務處。在幾十年前沒有電腦的年代,人們是怎麼「申請獎學金」的?假設你準備要申請獎學金了,你在家填好申請單,走進教務處,接下來發生的事會有哪些?我們來試著在腦中演練一下…

首先,門口會有一位工讀生上前招呼,並且詢問你的來意。在了解你的來意後,會先檢查你的申請表是否有確實填寫,以免你因為一些資料不完整而被退件,浪費教務處專員的時間,以及,當然,你的等待時間。

接著,工讀生會引導你去一個寫著「獎學金申請」的櫃台前椅子上坐下來排隊等候。等輪到你時,負責的專員會叫你的名字,請你上前辦理。專員會接過你的申請單,接著做一連串動作:

  1. 調出相關資料:專員會需要你的學籍資料,以及該獎學金的規章與現在申請狀況。這些資料不在他手上,他得轉頭向一位(或一些)檔案管理員調閱,而檔案管理員收到請求後,則是轉身從身後的檔案櫃翻找出你的資料。在過去沒有電腦的年代,他們只能這麼做。
  2. 確認身份與資料:這裡的確認與前台工讀生的不同,因為這裡不只是檢查表格有沒有填正確,而是要確保你是本校學生、申請的獎學金資格相符、申請日沒有過期等。這些資料是工讀生沒有權限查詢的。
  3. 填寫官方記錄:你帶來的申請單是自己寫的,沒有經過官方認證,也沒有關防,不算正式記錄,它只能提供訊息給專員參考。待專員經過上述的查閱資料與審核後,如果一切沒問題,專員就需要撰寫一份「正式的、官方的」申請記錄,並在上面署名且蓋上關防。
  4. 存檔並回覆:最後,專員會把這份正式文件轉頭交給身後的檔案管理員,收入檔案櫃,正式成案,再開立一張收據給你,完成申請流程。

在以前沒有電腦的年代,(我猜想)學校的教務處就是這麼處理「申請獎學金」的工作的。

戲如人生

回到現代,我們的物件導向程式該怎麼模擬上述的流程?該創建哪些物件?這些物件又該被放在 Clean Architecture 的哪一層?以下是筆者自己的分析結果,各位可以參考一下:

登場人物 任務 物件名 分層
工讀生 詢問來者意圖,初步檢查文件完整性,為其帶路,找到合適的專員 ApplyScholarshipController Adapter
申請書 寫著申請資料,專員審核時可以參考 ApplicationForm UI/Adapter
「獎學金申請」專員 控管流程,根據申請書的資料,向檔案管理員索取資料,依規定審核後填寫正式記錄 ApplyScholarshipService Use Case
檔案管理員 依專員需求,找出資料或儲存資料 ScholarshipRepository Use Case / Adapter
「獎學金申請」正式記錄 記載此申請的官方資料與核心運算(如有需要) ScholarshipApplication Entity
檔案櫃 存放檔案 - DB

上述是筆者針對真實世界的案例,分析出的一些重要物件、其主要任務,與其在 Clean Architecture 階層架構中的所在位置。

下一篇起,我們會從 Interface Adapter 開始,一層一層地往下實作與測試,大致上會依照上表的規劃,但如有需要修正或重構,將會當場修正。

謎之聲:「人生如戲,戲如人生。」

tags: ithelp2021

上一篇
Day 21 「事有經重緩急」Clean Architecture 簡易入門
下一篇
Day 23 「啟動!Outside-In 之路」Controller 與單元測試
系列文
你就是都不寫測試才會沒時間:Kuma 的 30 天 Unit Test 手把手教學,從理論到實戰 (Java 篇)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Hoshikawa
iT邦新手 5 級 ‧ 2022-05-30 22:22:43

你好 想請問一下
基於上述的案例,如果想要追加 申請次數限制的功能

有兩個想法

  1. 寫一個新用例 ApplyScholarshipServiceWithCounter,內容將會跟 ApplyScholarshipService 大致相同,但多了對 Counter(Entity)的檢查,這樣的部分重複是能接受的嗎?

  2. 寫一個新用例 CheckCounter,就是想法1檢查的部分,將他分離出來,由 ApplyScholarshipController 呼叫,未通過則不執行 ApplyScholarshipService

想請問作者會如何追加這個功能?
謝謝

看更多先前的回應...收起先前的回應...
Kuma iT邦新手 3 級 ‧ 2022-05-30 22:47:56 檢舉

首先感謝您的提問,這的確是日常生活中蠻常遇到的問題。
如果沒誤會,您的意思應該是指「申請者不可以一個獎學金申情很多次」吧?
如果是的話,我認為這在現實生活中,應該可以由「承辦專員」發起,亦即,他請另一個管申請單的管理員找看看這位同學申請的記錄,然後後面的事應該就可以繼續了。

您的 Solution 1 我覺得蠻可行的,但您所謂的「重複」是什麼意思我就不解了,如果學校的獎學金政策有所修改,那麼承辦專員應該要知道,因此直接改 Service 應該就能解決問題。反正有測試,記得測試一併修改就好 :)

至於 Solution 2,我會覺得,讓門口工讀生去找記錄管理員要同學申請記錄,也不是不行,但一般較有記律的學校應該要避免這種事發生,所以我覺得如非必要,否則不要這樣寫程式,不然會與現實不符。

希望有回答到您的問題 :)

Hoshikawa iT邦新手 5 級 ‧ 2022-05-30 23:24:37 檢舉

感謝您的回答
申請次數限制是指一天能申請的上限次數(可能申請沒過被打回票)
不過理解成「申請者不可以一個獎學金申情很多次」也沒有問題

Solution 1 的重複是指 ApplyScholarshipService 也保留了而不是修改,因為假設了 Service/ServiceWithCounter 會作切換(流量緊張時),所以才有重複之說(兩者很像僅差一個判斷),但這兩種 Usecase是不是應該看作獨立的功能會比較適當?

Solution 2 讓表示層作這個判斷確實不妥

Hoshikawa iT邦新手 5 級 ‧ 2022-05-31 21:28:59 檢舉

整理了一下思緒 (考慮服務是能切換的)
以現實的角度來看

  1. 以學生(UserInput)的角度來看他不知道 現在是哪種服務
  2. 工讀生(Controller)應該知道現在提供什麼服務(持有ServiceFactory,工廠回傳IService,Service/ServiceWithCounter則實作IService),這樣Controller就回歸到轉接(Adapter)身上了
    2^. 或者是維持一個Service,但在Service內引入檢查員(IList)他們能先過濾資料,都通過則執行操作

比較偏好 2* 這樣日後有新增其他(前置檢查)機制時 Service不用做更動,寫一個新的 IValidator注入即可。

但 2的作法可能比較務實(?),要啥寫啥,畢竟引入IValidator是我對系統未來變更的賭注

Kuma iT邦新手 3 級 ‧ 2022-06-05 21:08:44 檢舉

我會覺得這些原則都是通則而不是規定,我寫這系列文章目的也是為了推廣 Unit Test 的習慣與乾淨的架構。

什麼層做什麼事,不做什麼事,其實我也覺得大家習慣就好,說不定兩年後四層就落伍了,六層當道也不一定 XD

我覺得您的考量我沒有評論的立場,畢這只是一個例子,我也不是什麼大神。只要大家都有寫測試,幾個月後覺得不適合了再來改,也是很簡單的事,對吧 :)

Kuma iT邦新手 3 級 ‧ 2022-06-05 21:10:20 檢舉

但我其實蠻喜你最後那個 IValidator 的 idea 的 :)

我要留言

立即登入留言